﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace WebRamosUC.Models
{
    public class RepositorioCurso : WebRamosUC.Models.IRepositorioCurso
    {
        WebRamosUCDataContext db;

        public RepositorioCurso()
        {
              db = new WebRamosUCDataContext();
        }

        public RepositorioCurso(WebRamosUCDataContext dataContext)
        {
              this.db = dataContext;
        }

     
        /// <summary>
        /// Retorna todos los cursos.
        /// </summary>
        public IQueryable<Curso> AllCursos()
        {
            return db.Cursos;
        }

        /// <summary>
        /// Retorna todos los cursos ordenados por nombre
        /// </summary>
        public IQueryable<Curso> AllCursos_orderby_nombre()
        {
            return from curso in db.Cursos
                   orderby curso.NombreCurso
                   select curso;
        }

        /// <summary>
        /// Retorna todos los cursos ordenados por facultad
        /// </summary>
        public IQueryable<Curso> AllCursos_orderby_facultad()
        {
            return from curso in db.Cursos
                   orderby curso.IDFacultad
                   select curso;
        }

        public IQueryable<Curso> GetCursosFacultad(int idFacultad)
        {
            return from curso in db.Cursos
                   where curso.IDFacultad == idFacultad
                   orderby curso.Sigla
                   select curso;
        }

        /// <summary>
        /// Retorna cursos que coinciden con el nombre o sigla ingresado.
        /// </summary>
        /// <param name="nombreOsigla">Nombre o sigla del curso</param>
        /// <param name="exacto">True para busqueda exacta, false para resultados que contengan el string dado.</param>
        public IQueryable<Curso> GetCursos(string nombreOsigla, bool exacto)
        {
            if (exacto)
            {
                return from curso in db.Cursos
                       where curso.NombreCurso == nombreOsigla || curso.Sigla == nombreOsigla
                       orderby curso.Sigla
                       select curso;
            }
            else
            {
                return from curso in db.Cursos
                       where curso.NombreCurso.Contains(nombreOsigla) || curso.Sigla.Contains(nombreOsigla)
                       orderby curso.Sigla
                       select curso;
            }
        }

        /// <summary>
        /// Retorna todos los cursos dictados por un profesor
        /// </summary>
        /// <param name="profesor"></param>
        /// <returns></returns>
        public IQueryable<Curso> GetCursos(Profesor profesor)
        {
            return from curso in db.Cursos
                       where curso.Secciones
                        .Any(sec => sec.ProfesorSecciones
                            .Any(ps => ps.Profesor == profesor))        //Busqueda anidada x2
                       orderby curso.Sigla
                       select curso;
        }
               
        /// <summary>
        /// Retorna el curso con la sigla dada exacta. No es sensible a mayusculas/minusculas.
        /// </summary>
        public Curso GetCurso(string sigla)
        {
            if (sigla == null)
                return null;

            string siglaMayus = sigla.ToUpperInvariant();
            return db.Cursos.SingleOrDefault(d => d.Sigla == siglaMayus);
        }

        /// <summary>
        /// Retorna seccion con la sigla dada exacta (del periodo actual). No es sensible a mayusculas/minusculas.
        /// 
        /// </summary>
        public Seccion GetSeccion(string sigla, int seccion)
        {
            if (sigla == null)
                return null;

            //string siglaMayus = sigla.ToUpperInvariant();
            Periodo periodoActual = new RepositorioPeriodo().GetPeriodoActual();

            return db.Seccions.SingleOrDefault(d => (d.Sigla == sigla && d.NumSeccion == seccion && d.Periodo == periodoActual));

        }

        /// <summary>
        /// Retorna seccion con la id dada exacta.
        /// </summary>
        public Seccion GetSeccion( int id_seccion)
        {
            if (id_seccion < 0)
                return null;

            return db.Seccions.SingleOrDefault(d => d.IDSeccion == id_seccion);

        }

        /// <summary>
        /// Retorna todos los recomendaciones.
        /// </summary>
        public IQueryable<Recomendacion> AllRecomendaciones(string sigla, int seccion)
        {
            string siglaMayus = sigla.ToUpperInvariant();
            Seccion sec =  db.Seccions.SingleOrDefault(d => (d.Sigla == siglaMayus && d.NumSeccion == seccion));

            return from recomendaciones in db.Recomendacions
                   where recomendaciones.IDSeccion == sec.IDSeccion
                   select recomendaciones;

        }

        /// <summary>
        /// Retorna recomendacion con la id dada exacta.
        /// </summary>
        public Recomendacion GetRecomendacion(int id_recomendacion)
        {
            if (id_recomendacion < 0)
                return null;

            return db.Recomendacions.SingleOrDefault(d => d.IDRecomendacion == id_recomendacion);

        }

        /// <summary>
        /// Agrega un curso al repositorio.
        /// </summary>
        public bool AddCurso(Curso curso)
        {
            db.Cursos.InsertOnSubmit(curso);

            return true;
        }

        /// <summary>
        /// Agrega un curso al repositorio.
        /// </summary>
        public bool AddRecomendacion(Recomendacion recomendacion)
        {
            db.Recomendacions.InsertOnSubmit(recomendacion);
            Save();
            return true;
        }
        
        /// <summary>
        /// revisa si el usuario ya hizo una recomendacion
        /// </summary>
        public bool UserHasAlreadyPost(int id_seccion, string nombre_usuario)
        {
            var usuario = db.Usuarios.SingleOrDefault(d=> d.Nombre == nombre_usuario);
            if(usuario == null)
                return false;
            
            int id_usuario = usuario.IDUsuario;

            IQueryable<Recomendacion> recomendaciones = from recomendacion in db.Recomendacions
                                  where recomendacion.Activo == true && recomendacion.IDSeccion == id_seccion && recomendacion.IDUsuario == id_usuario
                                  select recomendacion;

            if (recomendaciones.Count() == 0 )
                return true;
            else
                return false;

        }

        #region Rankings

        /// <summary>
        /// Retorna los ultimos cursos modificados.
        /// </summary>
        public IQueryable<Curso> GetCursosModificados(int cantidad)
        {
            var ultimos = from cursos in db.Cursos
                          orderby cursos.FechaModificacion descending
                          select cursos;
            return ultimos.Take(cantidad);
        }

        /// <summary>
        /// Retorna los ultimos cursos modificados  de una Facultad
        /// </summary>
        public IQueryable<Curso> GetCursosModificados(int cantidad, int idFacultad)
        {
            var ultimos = from cursos in db.Cursos
                          where cursos.IDFacultad == idFacultad
                          orderby cursos.FechaModificacion descending
                          select cursos;
            return ultimos.Take(cantidad);
        }

        /// <summary>
        /// Retorna los cursos más populares.
        /// </summary>
        /// <param name="cantidad">Cantidad de cursos a retornar.</param>
        public IQueryable<Curso> GetCursosPopulares(int cantidad)
        {
            var cursosPop = from curso in db.Cursos
                            join s in db.Seccions on curso.Sigla equals s.Sigla
                            join r in db.Recomendacions.Where(rec => rec.Activo) on s.IDSeccion equals r.IDSeccion
                            group curso by curso.Sigla into g
                            orderby g.Count() descending
                            select g;

            List<Curso> cursosPopulares = new List<Curso>();
            //Itero por todos cursos de 10 comentarios, 9 comentarios, 8, etc..
            foreach (var curso in cursosPop)
            {
                //Los agrego a la lista.
                cursosPopulares.Add(curso.First());
            }

            return cursosPopulares.Take(cantidad).AsQueryable();
        }

        /// <summary>
        /// Retorna los cursos más populares de una Facultad
        /// </summary>
        /// <param name="cantidad">Cantidad de cursos a retornar.</param>
        public IQueryable<Curso> GetCursosPopulares(int cantidad, int idFacultad)
        {
            var cursosPop = from curso in db.Cursos
                            where curso.IDFacultad == idFacultad
                            join s in db.Seccions on curso.Sigla equals s.Sigla
                            join r in db.Recomendacions.Where(rec => rec.Activo) on s.IDSeccion equals r.IDSeccion
                            group curso by curso.Sigla into g
                            orderby g.Count() descending
                            select g;

            List<Curso> cursosPopulares = new List<Curso>();
            //Itero por todos cursos de 10 comentarios, 9 comentarios, 8, etc..
            foreach (var curso in cursosPop)
            {
                //Los agrego a la lista.
                cursosPopulares.Add(curso.First());
            }

            return cursosPopulares.Take(cantidad).AsQueryable();
        }

        /// <summary>
        /// Reorna los cursos con mejor calificacion promedio, cierta cantidad
        /// </summary>
        public IEnumerable<Curso> GetCursosMejorEvaluados(int cantidad)
        {
            //En teoria esto elige las mejores secciones, en vez de cursos. Pero asi el codigo es mucho mas corto.
            var mejores = from seccion in db.Seccions
                          group seccion by seccion.Recomendaciones.Where(rec => rec.Activo).Average(r => (int?)r.Valor) into g
                          orderby g.Key descending
                          select new { Valor = g.Key, Seccion = g };


            List<Curso> result = new List<Curso>();

            //Iteramos para sacar los "top 10"
            foreach (var group in mejores)
            {
                foreach (var secc in group.Seccion)
                {
                    if (!result.Contains(secc.Curso))
                    {
                        result.Add(secc.Curso); //Nos quedamos con el curso en vez de la seccion

                        if (result.Count >= cantidad)
                            return result;
                    }
                }
            }

            return result;
        }

        /// <summary>
        /// Reorna los cursos con mejor calificacion promedio de una Facultad
        /// </summary>
        public IEnumerable<Curso> GetCursosMejorEvaluados(int cantidad, int idFacultad)
        {
            //En teoria esto elige las mejores secciones, en vez de cursos. Pero asi el codigo es mucho mas corto.
            var mejores = from seccion in db.Seccions
                          where seccion.Curso.IDFacultad == idFacultad
                          group seccion by seccion.Recomendaciones.Where(rec => rec.Activo).Average(r => (int?)r.Valor) into g
                          orderby g.Key descending
                          select new { Valor = g.Key, Seccion = g };


            List<Curso> result = new List<Curso>();

            //Iteramos para sacar los "top 10"
            foreach (var group in mejores)
            {
                foreach (var secc in group.Seccion)
                {
                    if (!result.Contains(secc.Curso))
                    {
                        result.Add(secc.Curso); //Nos quedamos con el curso en vez de la seccion

                        if (result.Count >= cantidad)
                            return result;
                    }
                }
            }

            return result;
        }

        #endregion

        /// <summary>
        /// Guarda los cambios.
        /// </summary>
        public void Save()
        {
            db.SubmitChanges();
        }
               
    }
}